package com.huanbao.gateway.util;


import com.huanbao.common.api.ResultCode;
import com.huanbao.common.exception.GateWayException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;

/* ━━━━━━佛祖保佑━━━━━━
 *                  ,;,,;
 *                ,;;'(    社
 *      __      ,;;' ' \   会
 *   /'  '\'~~'~' \ /'\.)  主
 * ,;(      )    /  |.     义
 *,;' \    /-.,,(   ) \    码
 *     ) /       ) / )|    农
 *     ||        ||  \)
 *     (_\       (_\
 * ━━━━━━永无BUG━━━━━━
 * @author ：zjq
 * @date ：2020/7/24 9:35
 * @description: TODO
 * @version: V1.0
 * @slogan: 天下风云出我辈，一入代码岁月催
 */
@Component
@Slf4j
public class JwtUtil {

    /**
     * 认证服务器许可我们的网关的clientId(需要在oauth_client_details表中配置)
     */
    @Value("${auth.client_id}")
    private String CLIENT_ID;

    /**
     * 认证服务器许可我们的网关的client_secret(需要在oauth_client_details表中配置)
     */
    @Value("${auth.client_secret}")
    private String CLIENT_SECRET;

    /**
     * 认证服务器暴露的获取token_key的地址
     */
    @Value("${auth.auth_token_key_url}")
    private String AUTH_TOKEN_KEY_URL;

    /**
     * 请求头中的 token的开始
     */
    private static final String AUTH_HEADER = "bearer ";


    /**
     * 去  认证中心  拿 公匙
     *
     * @param restTemplate
     * @return
     */
    public PublicKey getPublicKey(RestTemplate restTemplate) {

        String tokenKey = getTokenKeyByRemoteCall(restTemplate);

        try {

            //把获取的公钥开头和结尾替换掉
            String dealTokenKey = tokenKey.replaceAll("\\-*BEGIN PUBLIC KEY\\-*", "").replaceAll("\\-*END PUBLIC KEY\\-*", "").trim();

            java.security.Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(dealTokenKey));

            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            PublicKey publicKey = keyFactory.generatePublic(pubKeySpec);

            log.info("生成公钥:{}", publicKey);

            return publicKey;

        } catch (Exception e) {

            log.info("生成公钥异常:{}", e.getMessage());

            throw new GateWayException(ResultCode.GEN_PUBLIC_KEY_ERROR);
        }
    }


    /**
     * 方法实现说明: 通过远程调用获取认证服务器颁发 jwt 的 解析的key
     *
     * @param restTemplate 远程调用的操作类
     * @author:smlz
     * @return: tokenKey 解析jwt的tokenKey
     */
    private String getTokenKeyByRemoteCall(RestTemplate restTemplate) {
        // 封装请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth(CLIENT_ID, CLIENT_SECRET);  // 配置在  oauth_client_details 中第三方服务
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(null, headers);

        //第二步:远程调用获取token_key
        try {

            ResponseEntity<Map> response = restTemplate.exchange(AUTH_TOKEN_KEY_URL, HttpMethod.GET, entity, Map.class);

            String tokenKey = response.getBody().get("value").toString();

            log.info("去认证服务器获取Token_Key:{}", tokenKey);

            return tokenKey;
        } catch (Exception e) {
            log.error("远程调用认证服务器获取Token_Key失败:{}", e.getMessage());
            throw new GateWayException(ResultCode.GET_TOKEN_KEY_ERROR);
        }

    }

    /**
     * 校验 jwt
     *
     * @param authHeader
     * @param publicKey
     * @return Claims
     */
    public Claims validateJwtToken(String authHeader, PublicKey publicKey) {

        String token = null;
        try {
            token = StringUtils.substringAfter(authHeader, AUTH_HEADER);

            Jwt<JwsHeader, Claims> parseClaimsJwt = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);

            Claims claims = parseClaimsJwt.getBody();

            return claims;

        } catch (Exception e) {

            log.error("校验token异常:{},异常信息:{}", token, e.getMessage());

            throw new GateWayException(ResultCode.JWT_TOKEN_EXPIRE);
        }
    }
}


